package com.vincent.redispractise.part02.Idworker;

import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;

/**
* @Des  订单号全局唯一ID生成器
* @Auther  Vincent
* @Date  2022/11/30 23:47
*/
@Component
public class IdWorker {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    private final static Long STARTTIME = 1262304000L;

    /**
     * @Des 获取全局唯一的id
     * 需要考虑的几个方面
     *      唯一性：保证不重复
     *      高性能：全局并发获取，保证高性能
     *      高可用：不能应该单点故障影响全局业务
     *      递增性：方便订单的排序和统计
     *      安全性：不能明文显示订单信息导致订单数量泄漏
     * @solution 设计方法
     *      使用一个64位的数字，符号位+31为时间戳+32订单号，保证排序的同时便于订单统计
     * @Param
     * @Param
     * @Return
     * @Date 2022/12/1 0:01
     */
    public Long nextId(String businessKey){
        //1.获取一个当前的时间戳[为了安全不易猜，时间戳的起始时间可以从一个自定义的年份开始]
        LocalDateTime now = LocalDateTime.now();
        Long time = now.toEpochSecond(ZoneOffset.UTC) - STARTTIME;
        /*2.使用redis的自增递增订单号
            考虑订单位数不够用：不同业务使用不同的redis自增key分别累加
            考虑每日订单统计方便：redis的自定key可以设置为形如：icr+业务类型+yyyy:MM:dd
        */
        String date = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));
        Long count = stringRedisTemplate.opsForValue().increment("incr:" + businessKey + ":" + date);
        //3.组装id,使用位运算|[至少一个为真则为真]
        return time<<32 |count;
    }

    public static void main(String[] args) {
        long startTime = LocalDateTime.of(2010, 1, 1, 0, 0, 0).toEpochSecond(ZoneOffset.UTC);
        System.out.println(startTime);//1262304000

    }
}
